using System;
using System.Collections.Generic;
using Kopernikus.Collections.Generic;
using Kopernikus.Parsing.GrammarSets;
using Kopernikus.Parsing.Utility;

namespace Kopernikus.Parsing
{
    public class Grammar : IGrammar, IGrammarSymbols, IGrammarProductions
    {
        private readonly LazyMember<FirstSets> m_firstSets;
        private readonly Dictionary<Production, Production> m_mappedProductions;
        private readonly LazyMember<NullableSymbolsAndProductions> m_nullableSymbolsAndProductions;
        private readonly Set<Production> m_productions;

        private readonly LazyMember<ProductionsByLHS> m_productionsByLHS;
        private readonly LazyMember<ProductionsByRHS> m_productionsByRHS;
        private readonly LazyMember<SententialSymbolsAndProductions> m_sententialSymbolsAndProductions;
        private readonly Set<Production> m_startProductions;
        private readonly LazyMember<TerminalAndNonterminalSymbols> m_terminalAndNonterminalSymbols;

        public Grammar(Set<Production> startProductions, Set<Production> productions)
            : this(startProductions, productions, null)
        {
        }

        public Grammar(Set<Production> startProductions, Set<Production> productions,
                       Dictionary<Production, Production> mappedProductions)
        {
            CompressedGrammar compressedGrammar =
                new CompressedGrammar(startProductions, productions.Union(startProductions));
            m_startProductions = compressedGrammar.CompressedStartProductions;
            m_productions = compressedGrammar.CompressedProductions;

            m_mappedProductions = new Dictionary<Production, Production>();
            if (mappedProductions != null)
            {
                foreach (KeyValuePair<Production, Production> pair in mappedProductions)
                {
                    if (m_productions.Contains(pair.Key))
                    {
                        m_mappedProductions[pair.Key] = pair.Value;
                    }
                }
            }

            m_productions = new ReachableGrammarProductions(this).ReachableProductions;

            m_terminalAndNonterminalSymbols =
                new LazyMember<TerminalAndNonterminalSymbols>(
                    delegate { return new TerminalAndNonterminalSymbols(this); });
            m_productionsByLHS = new LazyMember<ProductionsByLHS>(delegate { return new ProductionsByLHS(this); });
            m_productionsByRHS = new LazyMember<ProductionsByRHS>(delegate { return new ProductionsByRHS(this); });
            m_nullableSymbolsAndProductions =
                new LazyMember<NullableSymbolsAndProductions>(
                    delegate { return new NullableSymbolsAndProductions(this); });

            m_sententialSymbolsAndProductions =
                new LazyMember<SententialSymbolsAndProductions>(
                    delegate { return new SententialSymbolsAndProductions(this); });

            m_firstSets = new LazyMember<FirstSets>(delegate { return new FirstSets(this); });
        }

        #region IGrammar Members

        public Set<Production> StartProductions
        {
            get { return m_startProductions; }
        }

        public Set<Production> Productions
        {
            get { return m_productions; }
        }

        public Set<Symbol> Symbols
        {
            get { return m_terminalAndNonterminalSymbols.Member.AllSymbols; }
        }

        public IGrammarProductions ProductionSets
        {
            get { return this; }
        }

        public IGrammarSymbols SymbolSets
        {
            get { return this; }
        }

        public Production MapProduction(Production p)
        {
            Production mappedProduction;
            return m_mappedProductions.TryGetValue(p, out mappedProduction) ? mappedProduction : p;
        }

        #endregion

        #region IGrammarProductions Members

        public Set<Production> SententialProductions
        {
            get { return m_sententialSymbolsAndProductions.Member.SententialProductions; }
        }

        public Set<Production> NullableProductions
        {
            get { return m_nullableSymbolsAndProductions.Member.Productions; }
        }

        public Set<Production> GetProductionsByLHS(Symbol s)
        {
            return m_productionsByLHS.Member.GetProductionsByLHS(s);
        }

        public Set<Production> GetProductionsByRHS(Symbol s)
        {
            return m_productionsByRHS.Member.GetProductionsByRHS(s);
        }

        public Production GetNullableProductionByLHS(Symbol s)
        {
            return m_productionsByLHS.Member.GetNullableProductionByLHS(s);
        }

        #endregion

        #region IGrammarSymbols Members

        public Set<Symbol> NullableSymbols
        {
            get { return m_nullableSymbolsAndProductions.Member.Symbols; }
        }

        public Set<Symbol> TerminalSymbols
        {
            get { return m_terminalAndNonterminalSymbols.Member.TerminalSymbols; }
        }

        public Set<Symbol> NonTerminalSymbols
        {
            get { return m_terminalAndNonterminalSymbols.Member.NonTerminalSymbols; }
        }

        public Set<Symbol> GetFirst(Symbol s)
        {
            return m_firstSets.Member.GetFirst(s);
        }

        public Set<Symbol> GetFollow(Symbol s)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}